﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using AGPR.Locadora.Data;
using AGPR.Locadora.Utils;

namespace AGPR.Locadora.Model
{
    /// <summary>
    /// Classe de Locação
    /// </summary>
    public class Locacao
    {
        private static ActiveRecord db = ActiveRecord.Instancia;

        #region Atributos

        /// <summary>
        /// Status Alugado
        /// </summary>
        public const int STATUS_ALUGADO = 1;
        /// <summary>
        /// Status Devolvido
        /// </summary>
        public const int STATUS_DEVOLVIDO = 3;
        /// <summary>
        /// Status Cancelado
        /// </summary>
        public const int STATUS_CANCELADO = 5;

        #region Database

        /// <summary>
        /// Tabela de Locação
        /// </summary>
        public const string TABELA = "locacao";

        /// <summary>
        /// VIEW da entidade Locação
        /// </summary>
        public const string VIEW = "vw_locacao";

        #endregion

        #region Código da Locação

        /// <summary>
        /// Código Interno da Locação
        /// </summary>
        private int _id_locacao;

        /// <summary>
        /// Código da Locação
        /// </summary>
        public int id_locacao
        {
            get
            {
                return this._id_locacao;
            }
            set
            {
                this._id_locacao = value;
                this.produtos = LocacaoProduto.GetAll(value);
            }
        }

        #endregion

        #region Cliente

        /// <summary>
        /// Objeto do Cliente
        /// </summary>
        public Cliente cliente;

        /// <summary>
        /// Código Interno do Cliente
        /// </summary>
        private int _id_cliente;

        /// <summary>
        /// Código do Cliente
        /// </summary>
        public int id_cliente
        {
            get
            {
                return this._id_cliente;
            }
            set
            {
                this._id_cliente = value;
                this.cliente = Cliente.Get(value);
            }
        }

        #endregion

        #region Funcionário

        /// <summary>
        /// Objeto do Funcionário
        /// </summary>
        public Funcionario funcionario;

        /// <summary>
        /// Código Interno do Funcionário
        /// </summary>
        private int _id_funcionario;

        /// <summary>
        /// Código do Funcionário
        /// </summary>
        public int id_funcionario
        {
            get
            {
                return this._id_funcionario;
            }
            set
            {
                this._id_funcionario = value;
                this.funcionario = Funcionario.Get(value);
            }
        }

        #endregion

        #region Status

        /// <summary>
        /// Objeto do Status
        /// </summary>
        public StatusLocacao status;

        /// <summary>
        /// Código Interno do Status
        /// </summary>
        private int _id_status;

        /// <summary>
        /// Código do Status
        /// </summary>
        public int id_status
        {
            get
            {
                return this._id_status;
            }
            set
            {
                this._id_status = value;
                this.status = StatusLocacao.Get(value);
            }
        }

        #endregion

        #region Produtos

        /// <summary>
        /// Produtos da Locação
        /// </summary>
        public List<LocacaoProduto> produtos = new List<LocacaoProduto>();

        #endregion

        #region Valor da Multa

        /// <summary>
        /// Valor da Multa Decimal
        /// </summary>
        public decimal valor_multa { get; set; }

        /// <summary>
        /// Valor da Multa Formatado
        /// </summary>
        public string valorMulta
        {
            get
            {
                return Format.formatarDinheiro(this.valor_multa);
            }
            set
            {
                this.valor_multa = Format.limparDecimal(value);
            }
        }

        #endregion

        #region Valor Pago

        /// <summary>
        /// Valor Pago Decimal
        /// </summary>
        public decimal valor_pago { get; set; }

        /// <summary>
        /// Valor Pago Formatado
        /// </summary>
        public string valorPago
        {
            get
            {
                return Format.formatarDinheiro(this.valor_pago);
            }
            set
            {
                this.valor_pago = Format.limparDecimal(value);
            }
        }

        #endregion

        #region Valor Total

        /// <summary>
        /// Valor Total da Locação
        /// </summary>
        public decimal valor_total
        {
            get
            {
                decimal total = 0;

                foreach (LocacaoProduto prod in this.produtos)
                {
                    total += prod.qtd * prod.valor;
                }

                return total;

            }
        }

        /// <summary>
        /// Valor Total da Locação Formatado
        /// </summary>
        public string valorTotal
        {
            get
            {
                return Format.formatarDinheiro(this.valor_total);
            }
        }

        #endregion

        #region Data de Devolução

        /// <summary>
        /// Data de Devolução
        /// </summary>
        public string data_devolucao { get; set; }

        #endregion

        #region Data de Locação

        /// <summary>
        /// Data da Locação
        /// </summary>
        public DateTime data_locacao { get; set; }

        #endregion

        #region Data Prevista de Devolução

        /// <summary>
        /// Data Prevista de Devolução (+1 Semana) (Regra de Negócio)
        /// </summary>
        public static string dataPrevista
        {
            get
            {
                return DateTime.Now.AddDays(7).ToString("dd/MM/yyyy");
            }
        }

        #endregion

        #region  Alugado

        /// <summary>
        /// Alugado
        /// </summary>
        public bool Alugado
        {
            get
            {
                return this.id_status == STATUS_ALUGADO;
            }
        }

        #endregion

        #region Devolvido

        /// <summary>
        /// Devolvido
        /// </summary>
        public bool Devolvido
        {
            get
            {
                return this.id_status == STATUS_DEVOLVIDO;
            }
        }

        #endregion

        #endregion

        #region getNextID

        /// <summary>
        /// Retorna o Proximo ID
        /// </summary>
        /// <returns>ID</returns>
        private int getNextID()
        {
            return db.max("id_locacao", TABELA) + 1;
        }

        #endregion

        #region ~Construtor

        /// <summary>
        /// Construtor
        /// </summary>
        public Locacao()
        {
            this.id_locacao = this.getNextID();
        }

        #endregion

        #region Create

        /// <summary>
        /// Retorna uma nova Locação
        /// </summary>
        /// <param name="dados">Dados</param>
        /// <returns>Locação</returns>
        public static Locacao Create(dynamic dados)
        {
            Locacao loc = new Locacao();

            if (dados != null)
            {
                loc.id_locacao = Convert.ToInt32(dados["id_locacao"]);
                loc.id_cliente = Convert.ToInt32(dados["id_cliente"]);
                loc.id_funcionario = Convert.ToInt32(dados["id_funcionario"]);
                loc.id_status = Convert.ToInt32(dados["id_status"]);
                loc.valor_multa = Convert.ToDecimal(dados["valor_multa"]);
                loc.valor_pago = Convert.ToDecimal(dados["valor_pago"]);
                loc.data_devolucao = dados["data_devolucao"];
                loc.data_locacao = Convert.ToDateTime(dados["data_locacao"]);
            }

            return loc;
        }

        #endregion

        #region Cadastrar

        /// <summary>
        /// Cadastrar uma Locação
        /// </summary>
        /// <returns>Locacao</returns>
        public Locacao Cadastrar()
        {
            db.insert(TABELA, new Dictionary<string, object>
            {
                {"id_locacao", this.id_locacao},
                {"id_cliente", this.id_cliente},
                {"id_funcionario", this.id_funcionario},
                {"id_status", STATUS_ALUGADO},
                {"data_devolucao", this.data_devolucao}
            });

            // Incluo todos os produtos na locação atual
            foreach (LocacaoProduto prod in this.produtos)
            {
                prod.Incluir();
            }

            return this;
        }

        #endregion

        #region Alterar

        /// <summary>
        /// Alterar uma Locação
        /// </summary>
        /// <returns>Locacao</returns>
        public Locacao Alterar()
        {
            db.where("id_locacao", this.id_locacao);
            db.update(TABELA, new Dictionary<string, object>
            {
                {"id_status", this.status},
                {"valor_multa", this.valor_multa},
                {"valor_pago", this.valor_pago},
                {"data_devolucao", this.data_devolucao}
            });

            // Incluo todos os produtos na locação atual
            foreach (LocacaoProduto prod in this.produtos)
            {
                prod.Alterar();
            }

            return this;
        }

        #endregion

        #region Devolver

        /// <summary>
        /// Devolve uma Locação
        /// </summary>
        /// <returns>Locacao</returns>
        public Locacao Devolver(dynamic dados)
        {
            this.id_status = STATUS_DEVOLVIDO;
            this.valorMulta = dados["valor_multa"];
            this.valorPago = dados["valor_pago"];
            this.data_devolucao = DateTime.Now.ToString("dd/MM/yyyy");

            db.where("id_locacao", this.id_locacao);
            db.update(TABELA, new Dictionary<string, object>
            {
                {"id_status", this.id_status},
                {"valor_multa", this.valor_multa},
                {"valor_pago", this.valor_pago},
                {"data_devolucao", this.data_devolucao}
            });

            return this;
        }

        #endregion

        #region Cancelar

        /// <summary>
        /// Cancelar uma Locação
        /// </summary>
        /// <returns>Locacao</returns>
        public Locacao Cancelar()
        {
            this.id_status = STATUS_CANCELADO;

            db.where("id_locacao", this.id_locacao);
            db.update(TABELA, "id_status", this.id_status);

            return this;
        }

        #endregion

        #region Get

        /// <summary>
        /// Retorna uma Locação pelo ID
        /// </summary>
        /// <param name="id">ID da Locação</param>
        /// <returns>Locacao</returns>
        public static Locacao Get(object id)
        {
            var item = db.where("id_locacao", id).selectOne(VIEW);

            return item != null ? Locacao.Create(item) : null;
        }

        /// <summary>
        /// Retorna uma lista de todas Locações
        /// </summary>
        /// <returns>Lista de Locações</returns>
        public static List<Locacao> GetAll(int offset = 0)
        {
            List<Locacao> itens = new List<Locacao>();

            db.limit(offset, Config.PageLimit).order_by("id_locacao", "DESC");

            foreach (var item in db.select(VIEW))
            {
                itens.Add(Locacao.Create(item));
            }

            return itens;
        }

        #endregion

        #region Total

        /// <summary>
        /// Retorna o Total de Locações Cadastrados
        /// </summary>
        /// <returns>Total</returns>
        public static int Total()
        {
            return db.count(TABELA);
        }

        #endregion
    }
}
